#include<iostream>
#include<fstream>
#include<cstdlib>
using namespace std;
const int PLINK_ID1 = 108;
const int PLINK_ID2 = 27;
const int PLINK_SNP_MAJOR=1;
const int PLINK_PERSON_MAJOR=0;
const int PLINK_WT_HOMO=0;
const int PLINK_MUT_HOMO=3;
const int PLINK_HET=2;
const int PLINK_MISSING=1;
const char GARY_MISSING=0;
const char GARY_WT_HOMO=1;
const char GARY_HET=2;
const char GARY_MUT_HOMO=3;

int major_mode;
int totalrows;
int totalcols;

void gary2plink(){
  //ifstream ifs("genotypes.txt",ios::in|ios::binary);
  //ofstream ofs("genotypes.bed",ios::out|ios::binary);
  //if (!ofs.is_open()){
  //if (!ifs.is_open() && !ofs.is_open()){
    //cerr<<"genotypes.txt not found or genotypes.bed could not open. exiting\n";
    //exit(1);
  //}
  char plink_header[3];
  plink_header[0] = PLINK_ID1;
  plink_header[1] = PLINK_ID2;
  plink_header[2] = major_mode;
  cout.write(plink_header,3);
  for (int row=0;row<totalrows;++row){
    int totalinlen = totalcols+1; // for EOF
    bool remainder = (totalcols % 4 !=0)?true:false;
    int totaloutlen = totalcols/4+remainder; // for remainder
    char inputvec[totalinlen];
    char outputvec[totaloutlen];
    cin.read(inputvec,totalcols+1);
    //ifs.read(inputvec,totalcols+1);
    int outputcount=0;
    int totalshifts = 0;
    for (int col=0;col<totalcols;++col){
      if (totalshifts==0) outputvec[outputcount] = 0;
      int plinkgeno;
      switch(inputvec[col]){
        case '1':
          plinkgeno = PLINK_WT_HOMO;
          break;
        case '2':
          plinkgeno = PLINK_HET;
          break;
        case '3':
          plinkgeno = PLINK_MUT_HOMO;
          break;
        case '0':
          plinkgeno = PLINK_MISSING;
          break;
      }
      //cerr<<"plinkgeno: "<<plinkgeno;
      //cerr<<"row "<<row<<" col "<<col<<" geno "<<inputvec[col]<<endl;
      //cerr<<"t_shifts "<<totalshifts<<" outputcount "<<outputcount<<" plinkgeno: "<<plinkgeno;
      //cerr<<" b_shift"<<plinkgeno;
      for(int shift=0;shift<totalshifts;++shift) plinkgeno = plinkgeno<<2;
      //cerr<<" a_shift"<<plinkgeno;
      outputvec[outputcount] = outputvec[outputcount] | plinkgeno;
      //outputvec[outputcount] = (outputvec[outputcount] | plinkgeno);
      //cerr<<" totalshifts: "<<totalshifts<<" after add: "<<(int)((unsigned char)outputvec[outputcount])<<endl;
      if (totalshifts==3){
        totalshifts = 0;
        ++outputcount;
      }else{
        ++totalshifts;
      }
    }
    cout.write(outputvec,totaloutlen);
    //ofs.write(outputvec,totaloutlen);
    cerr<<"Row "<<row<<" completed.\n";
  }
  //ifs.close();
  //ofs.close();
}

void testing2(){
  char testread[4];
  ifstream ifs("genotypes.imputed",ios::in|ios::binary);
  ifs.seekg(4,ios::cur);
  //ifs.read(testread,4);
  //for(int i=0;i<3;++i){
  //  cerr<<i<<" "<<testread[i]<<endl;
  //}
  ifs.seekg(4,ios::cur);
  ifs.read(testread,4);
  for(int i=0;i<3;++i){
    cerr<<i<<" "<<testread[i]<<endl;
  }
  ifs.seekg(4,ios::cur);
  ifs.seekg(4,ios::cur);
  ifs.seekg(4,ios::cur);
  if (!ifs.eof()) cerr<<"eof\n";
  ifs.close();
}

void testing(){
  char testread1[3];
  char testread2[4];
  ifstream ifs("genotypes.bed",ios::in|ios::binary);
  ifs.read(testread1,3);
  ifs.read(testread2,4);
  ifs.close();
  int masks[4] = {3,12,48,192};
  for(int i=0;i<4;++i){
    //if (i<3) cerr<<"PLINK ID: "<<(int)testread[i]<<endl;
    for(int j=0;j<4;++j){
      int val = masks[j] & testread2[i];
      for(int k=0;k<j;++k) val = val>>2;
      cerr<<i<<"\t"<<val<<endl;
    }
  }
  ofstream ofs("test.bin",ios::out|ios::binary);
  char  testchar[3] ;
  testchar[0]=1;
  testchar[1]=2;
  testchar[2]=3;
  ofs.write(testchar,3);
  testchar[0]=11;
  testchar[1]=21;
  testchar[2]=31;
  ofs.write(testchar,3);
  ofs.close();
}

int main(int argc,char * argv[]){
  if (argc<4){
    cerr<<"Arguments: <mode: 0=person,1=snp> <rows> <cols>\n";
    exit(1);
  }
  major_mode = atoi(argv[1]);
  if (major_mode!=PLINK_SNP_MAJOR && major_mode!=PLINK_PERSON_MAJOR){
     cerr<<"Invalid major mode.\n";
     exit(0);
  }
  totalrows = atoi(argv[2]);
  totalcols = atoi(argv[3]);
  gary2plink();
}
